#pragma once

#include <string>
#include <cstdint>
#include <odb/core.hxx>
#include <odb/nullable.hxx>
#include <boost/date_time/posix_time/posix_time.hpp>

namespace suye
{
// Outbox 事件状态定义
enum class OutboxStatus : std::uint8_t
{
    Pending = 0,
    Dispatching = 1,
    Completed = 2,
    Failed = 3
};

#pragma db object table("outbox_event")
class OutboxEvent
{
public:
    OutboxEvent() = default;

    OutboxEvent(const std::string& event_id,
                const std::string& aggregate_type,
                const std::string& aggregate_id,
                const std::string& event_type,
                const std::string& payload,
                OutboxStatus status = OutboxStatus::Pending,
                unsigned int max_retry = 5,
                const boost::posix_time::ptime& available_at = boost::posix_time::second_clock::universal_time())
        : _event_id(event_id)
        , _aggregate_type(aggregate_type)
        , _aggregate_id(aggregate_id)
        , _event_type(event_type)
        , _payload(payload)
        , _status(static_cast<unsigned char>(status))
        , _retry_count(0)
        , _max_retry(max_retry)
        , _available_at(available_at)
        , _created_at(boost::posix_time::second_clock::universal_time())
        , _updated_at(_created_at)
    {
    }

    ~OutboxEvent() = default;

    unsigned long id() const { return _id; }

    const std::string& event_id() const { return _event_id; }
    void event_id(const std::string& value) { _event_id = value; }

    const std::string& aggregate_type() const { return _aggregate_type; }
    void aggregate_type(const std::string& value) { _aggregate_type = value; }

    const std::string& aggregate_id() const { return _aggregate_id; }
    void aggregate_id(const std::string& value) { _aggregate_id = value; }

    const std::string& event_type() const { return _event_type; }
    void event_type(const std::string& value) { _event_type = value; }

    const std::string& payload() const { return _payload; }
    void payload(const std::string& value) { _payload = value; }

    OutboxStatus status() const { return static_cast<OutboxStatus>(_status); }
    void status(OutboxStatus value) { _status = static_cast<unsigned char>(value); }

    unsigned int retry_count() const { return _retry_count; }
    void retry_count(unsigned int value) { _retry_count = value; }

    unsigned int max_retry() const { return _max_retry; }
    void max_retry(unsigned int value) { _max_retry = value; }

    boost::posix_time::ptime available_at() const { return _available_at; }
    void available_at(const boost::posix_time::ptime& value) { _available_at = value; }

    boost::posix_time::ptime created_at() const { return _created_at; }
    void created_at(const boost::posix_time::ptime& value) { _created_at = value; }

    boost::posix_time::ptime updated_at() const { return _updated_at; }
    void updated_at(const boost::posix_time::ptime& value) { _updated_at = value; }

    std::string last_error() const
    {
        if (!_last_error)
        {
            return std::string();
        }
        return *_last_error;
    }
    void last_error(const std::string& value) { _last_error = value; }

private:
    friend class odb::access;

    #pragma db id auto
    unsigned long _id;

    #pragma db type("varchar(64)") index unique
    std::string _event_id;

    #pragma db type("varchar(64)") index
    std::string _aggregate_type;

    #pragma db type("varchar(64)") index
    std::string _aggregate_id;

    #pragma db type("varchar(32)")
    std::string _event_type;

    #pragma db type("MEDIUMTEXT")
    std::string _payload;

    #pragma db type("TINYINT UNSIGNED")
    unsigned char _status{static_cast<unsigned char>(OutboxStatus::Pending)};

    #pragma db type("INT UNSIGNED")
    unsigned int _retry_count{0};

    #pragma db type("INT UNSIGNED")
    unsigned int _max_retry{5};

    #pragma db type("TIMESTAMP")
    boost::posix_time::ptime _available_at{boost::posix_time::second_clock::universal_time()};

    #pragma db type("TIMESTAMP")
    boost::posix_time::ptime _created_at{boost::posix_time::second_clock::universal_time()};

    #pragma db type("TIMESTAMP")
    boost::posix_time::ptime _updated_at{boost::posix_time::second_clock::universal_time()};

    #pragma db type("TEXT")
    odb::nullable<std::string> _last_error;
};
}

// odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time outbox_event.hxx
